import json
import time
import asyncio
import websockets
from queue import Queue
from threading import Thread, Lock

from src.user_data import UserData
from src.logger import Logger


class WsHandler:
    def __init__(self, conf: UserData, logger: Logger):
        """处理与机器人的ws连接"""
        self.__logger = logger
        self.__conf = conf
        self.__ini = self.__conf.iniCls
        self.__token = f"?access_token={self.__ini.robot_key}"
        self.__host = f"ws://{self.__ini.robot_host}:{self.__ini.robot_port}"
        # 内部参数
        self.__task = Queue()
        self.__recv = Queue()
        self.__api_lock = Lock()
        self.alive = False
        self.event = Queue()
        self.__failed_times = 0
        self.__thread_list: list = []

    def do_cmd(self, action: str, params: dict = None, echo="") -> dict:
        """提供给程序进行api交互"""
        if params is None:
            params = {}
        task = json.dumps({"action": action, "params": params, "echo": echo})
        with self.__api_lock:
            self.__task.put(task)
            res: dict = self.__recv.get(timeout=5)
        return res

    def __recv_event(self) -> None:
        """接收来自机器人的通知"""
        async def recv_event():
            try:
                async with websockets.connect(self.__host + self.__token) as websocket:
                    self.alive = True
                    if self.__failed_times != 0:
                        self.__logger.add_log("[GLF机器人] 机器人重新连接成功")
                        self.__failed_times = 0
                    while True:
                        res: dict = json.loads(await websocket.recv())
                        self.event.put(res)
            except Exception:
                self.alive = False
                if self.__failed_times < 2:
                    self.__logger.add_log("[GLF机器人] 机器人连接失败或中断")
                    self.__failed_times += 1
                time.sleep(2)
                self.__init_thread(self.__recv_event)

        asyncio.run(recv_event())

    def __communicate(self) -> None:
        """发送指令与获取返回结果"""
        async def deal_cmd():
            try:
                async with websockets.connect(self.__host + "/api" + self.__token) as websocket:
                    while True:
                        await websocket.send(self.__task.get())
                        res: dict = json.loads(await websocket.recv())
                        self.__recv.put(res)
            except Exception:
                time.sleep(2)
                self.__init_thread(self.__communicate)

        asyncio.run(deal_cmd())

    def __init_thread(self, func) -> None:
        th = Thread(target=func, daemon=True)
        self.__thread_list.append(th)
        th.start()

    def start(self) -> None:
        self.__init_thread(self.__communicate)
        self.__init_thread(self.__recv_event)

    def reload(self) -> None:
        self.__ini = self.__conf.iniCls
        self.__token = f"?access_token={self.__ini.robot_key}"
        self.__host = f"ws://{self.__ini.robot_host}:{self.__ini.robot_port}"
